#pragma once

#include <cstdint>
#include <ctime>
#include <functional>
#include <deque>
#include "protocol_analyzer.hh"
#include "module_define.hh"
#include "../corelib/network/thirdparty/khttp/interface/http.h"
#include "../corelib/object_pool.hpp"
#include "../corelib/grid/site_id.hh"
#include "../corelib/util/allocator.hh"

namespace kconfig {
    class Config;
}

namespace kratos { namespace corelib {
    class Log;
    struct Finish;
}}

class ModuleFrame;

namespace kratos { namespace engine {

using TimerHandle = std::uint32_t;
using PathID = std::uint64_t;
constexpr static PathID INVALID_PATH_ID = 0;
using SiteType = std::uint16_t;
constexpr static SiteType INVALID_SITE_TYPE = 0;
using SessionID = std::uint32_t;
constexpr static SessionID INVALID_SESSION_ID = 0;
using UserData = std::uint64_t;
using PathList = std::vector<PathID>;
using SiteList = std::vector<SiteType>;
using EntityIDQueue = std::deque<std::uint64_t>;

// Path selecting policy, reserved
enum class PathQuery : uint8_t {
    PATH_QUERY_ROUNDROBIN = 1,
    PATH_QUERY_RANDOM,
    PATH_QUERY_WEIGHT,
};

class ComponentManager;

// engine interface
class Engine {
public:
    // find a path to specified grid type
    // @param siteType grid type
    // @param userData user data reserved, set to zero
    // @retval true
    // @retval false fail
    virtual bool findPath(SiteType siteType, UserData userData) = 0;
    // Returns the grid site ID for path peer
    // @param pathID path ID
    // @return The grid site ID for path peer
    virtual const grid::SiteID& getPathPeerSiteID(PathID pathID) = 0;
    // close path
    // @param pathID path ID
    virtual void closePath(PathID pathID) = 0;
    // close gateway session
    // @param sessionID session ID
    virtual void closeSession(SessionID sessionID) = 0;
    // send protobuf::Message via path
    // @param pathID path ID
    // @param data data pointer
    // @param size data length
    // @retval true
    // @retval false fail
    virtual bool talkPath(PathID pathID, const char* data, std::size_t size) = 0;
    // send protobuf::Message via gateway session
    // @param sessionID session ID
    // @param data data pointer
    // @param size data length
    // @retval true
    // @retval false fail
    virtual bool talkSession(SessionID sessionID, const char* data, std::size_t size) = 0;
    // send protobuf::Message via site ID
    // @param siteID site ID
    // @param data data pointer
    // @param size data length
    // @retval true
    // @retval false fail
    virtual bool talkSite(const grid::SiteID& siteID, const char* data, std::size_t size) = 0;
    // Broadcast message to all locator
    // @param data data pointer
    // @param size data length
    // @retval true
    // @retval false fail
    virtual bool broadcastLoactor(const char* data, std::size_t size) = 0;
    // Returns locator's pathID for entity ID
    // @param entityID entity ID
    // @retval INVALID_PATH_ID not found
    // @retval  locator's path ID
    virtual PathID getLocator(std::uint64_t entityID) = 0;
    // Add entity to registry
    // @param pathID path ID
    // @param entityID entity ID
    // @param entityType entity type
    // @retval true
    // @retval false fail
    virtual bool addEntity(PathID pathID, std::uint64_t entityID, std::uint32_t entityType) = 0;
    // Remove entity from registry
    // @param pathID path ID
    // @param entityID entity ID
    // @retval true
    // @retval false fail
    virtual bool removeEntity(PathID pathID, std::uint64_t entityID) = 0;
    // Returns entity type by entity ID
    // @param entityID entity ID
    // @return entity type
    virtual std::uint32_t getEntityType(std::uint64_t entityID) = 0;
    // Returns all entity with type 'entityType'
    // @param entityType entity type
    virtual const EntityIDQueue& getEntities(std::uint32_t entityType) = 0;
    // Returns entity's path ID
    // @param entityID entity ID
    // @retval INVALID_PATH_ID not found
    // @retval  locator's path ID
    virtual PathID getEntityPathID(std::uint64_t entityID) = 0;
    // start timer
    // @param duration timer period in millionseconds
    // @retval INVALID_TIMER_ID start failed
    // @retval timer ID
    virtual TimerHandle startTimer(std::time_t duration) = 0;
    // start timer and stop it when timeout 
    // @param duration timer period in millionseconds
    // @retval INVALID_TIMER_ID start failed
    // @retval timer ID
    virtual TimerHandle startTimerOnce(std::time_t duration) = 0;
    // cancel timer
    // @param timerID timer ID
    virtual void cancelTimer(TimerHandle timerID) = 0;
    // returns protocol header analyzer
    // @return ProtocolAnalyzer instance
    virtual ProtocolAnalyzer* getProtocolAnalyzer() = 0;
    // returns configuration
    virtual kconfig::Config* getConfig() = 0;
    // returns verbose logger
    virtual kratos::corelib::Log& getLogverb() = 0;
    // returns debug logger
    virtual kratos::corelib::Log& getLogdebug() = 0;
    // returns warning logger
    virtual kratos::corelib::Log& getLogwarn() = 0;
    // returns error logger
    virtual kratos::corelib::Log& getLogerror() = 0;
    // returns fatal logger
    virtual kratos::corelib::Log& getLogfatal() = 0;
    // returns logger finisher
    virtual const kratos::corelib::Finish& getLogend() = 0;
    // returns new http request
    // @param cb callback
    // @param host remote host
    // @param uri URI
    // @param port remote server port
    // @retval nullptr fail
    // @retval khttp::HttpRequest instance
    virtual khttp::HttpRequest* newHttpRequest(khttp::HttpCallback* cb, const std::string& host, const std::string& uri, int32_t port = 80) = 0;
    // start a new http server
    // @param cb callback
    // @param host The host listening at
    // @param port The port listening at
    // @retval true
    // @retval false fail
    virtual bool newHttpServer(khttp::HttpCallback* cb, const std::string& host, int32_t port = 80) = 0;
    // send command to specific module and wait for result
    virtual std::string doCommand(const std::string& moduleName, const std::string& command) = 0;
    // send command to engine and wait for result
    virtual std::string doCommand(const std::string& command) = 0;
    // Returns engine's object pool manager
    virtual ObjectPoolManager& getObjectPoolManager() = 0;
    // Returns component manager
    virtual ComponentManager& getComponentManager() = 0;
    // Returns total heap size in memory pool
    virtual std::size_t getMemoryPoolSize() = 0;
    // Returns plugin's root path
    virtual const std::string& getPluginRootPath(const std::string& name) = 0;
    // Returns a new sequential UUID
    virtual std::uint64_t getUUID() = 0;
    // Returns a new random UUID
    virtual std::uint64_t getUUIDRandom() = 0;
    // Check if router
    virtual bool isRouter() = 0;
    // Connect to remote server
    virtual bool connect(const std::string& host, std::uint64_t userData, std::time_t timeout) = 0;
};

}}
